package com.koushikdutta.ion;

import com.example.ohosasynclibrary.async.AsyncServer;
import com.example.ohosasynclibrary.async.future.FutureCallback;
import com.example.ohosasynclibrary.async.util.FileCache;
import com.example.ohosasynclibrary.async.util.LogUtil;
import com.koushikdutta.ion.bitmap.BitmapInfo;
import com.koushikdutta.ion.bitmap.IonBitmapCache;
import com.koushikdutta.ion.bitmap.PostProcess;
import ohos.agp.utils.Point;
import ohos.media.image.ImagePacker;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;

import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.concurrent.CancellationException;

abstract class BitmapCallback {
    final String key;
    final Ion ion;

    public static void saveBitmapSnapshot(Ion ion, BitmapInfo info) {
        FileCache cache = ion.responseCache.getFileCache();
        if (info.bitmap == null || cache == null)
            return;
        File tempFile = cache.getTempFile();
        try {
            FileOutputStream out = new FileOutputStream(tempFile);
            // PixelMap转File文件 输出格式JPEG 质量100%
            ImagePacker imagePacker = ImagePacker.create();
            ImagePacker.PackingOptions packingOptions = new ImagePacker.PackingOptions();
            packingOptions.quality = 100;
            imagePacker.initializePacking(out, packingOptions);
            imagePacker.addImage(info.bitmap);
            imagePacker.finalizePacking();

            out.close();
            cache.commitTempFiles(info.key, tempFile);
        } catch (Exception ex) {
//            ex.printStackTrace();
            LogUtil.error(BitmapCallback.class.getName(), ex.getMessage());
        } finally {
            if (tempFile.delete()) {
                LogUtil.error(BitmapCallback.class.getName(), "file is delete");
            }
        }
    }

    public static void getBitmapSnapshot(final Ion ion, final String transformKey, final ArrayList<PostProcess> postProcess) {
        // don't do this if this is already loading
        if (ion.bitmapsPending.tag(transformKey) != null)
            return;
        final BitmapCallback callback = new LoadBitmapBase(ion, transformKey, true);
        Ion.getBitmapLoadExecutorService().execute(new Runnable() {
            @Override
            public void run() {
                if (ion.bitmapsPending.tag(transformKey) != callback) {
                    IonLog.d("IonBitmapLoader", "Bitmap cache load cancelled (no longer needed)");
                    return;
                }

                try {
                    File file = ion.responseCache.getFileCache().getFile(transformKey);
                    PixelMap bitmap = IonBitmapCache.loadBitmap(file, new ImageSource.DecodingOptions());
                    if (bitmap == null)
                        throw new Exception("Bitmap failed to load");
                    BitmapInfo info = new BitmapInfo(transformKey, "image/jpeg", bitmap, null);
                    info.servedFrom = ResponseServedFrom.LOADED_FROM_CACHE;

                    if (postProcess != null) {
                        for (PostProcess p : postProcess) {
                            p.postProcess(info);
                        }
                    }

                    callback.report(null, info);
                } catch (OutOfMemoryError e) {
                    callback.report(new Exception(e), null);
                } catch (Exception e) {
                    callback.report(e, null);
                    try {
                        ion.responseCache.getFileCache().remove(transformKey);
                    } catch (Exception ex) {
                        LogUtil.error(BitmapCallback.class.getName(), ex.getMessage());
                    }
                }
            }
        });
    }

    protected BitmapCallback(Ion ion, String key, boolean put) {
        this.key = key;
        this.put = put;
        this.ion = ion;

        ion.bitmapsPending.tag(key, this);
    }

    final boolean put;

    boolean put() {
        return put;
    }

    protected void onReported() {
        ion.processDeferred();
    }

    protected void report(final Exception e, final BitmapInfo info) {
        AsyncServer.post(Ion.mainHandler, () -> {
            BitmapInfo result = info;
            if (result == null) {
                // cache errors, unless they were cancellation exceptions
                result = new BitmapInfo(key, null, null, new Point());
                result.exception = e;
                if (!(e instanceof CancellationException))
                    ion.getBitmapCache().put(result);
            } else if (put()) {
                ion.getBitmapCache().put(result);
            } else {
                ion.getBitmapCache().putSoft(result);
            }

            final ArrayList<FutureCallback<BitmapInfo>> callbacks = ion.bitmapsPending.remove(key);
            if (callbacks == null || callbacks.size() == 0) {
                onReported();
                return;
            }
            for (FutureCallback<BitmapInfo> callback : callbacks) {
                callback.onCompleted(e, result);
            }
            onReported();
        });

        // attempt to smart cache stuff to disk

        if (info == null || info.originalSize == null
                // don't cache anything that requests not to be cached
                || !put
                // don't cache dead bitmaps
                || info.bitmap == null
                // don't cache gifs
                || info.gifDecoder != null
                // too big
                || info.sizeOf() > 512 * 512 * 4) {
            return;
        }

        saveBitmapSnapshot(ion, info);
    }
}
